#include <buola/gui.h>

#include <buola/image/ctextlayout.h>
#include <buola/image/cfont.h>
#include <buola/gui/cimagematrix.h>
#include <buola/gui/cconfig.h>
#include <buola/gui/cicon.h>
#include <buola/image/cimage.h>
#include <buola/image/cgc.h>
#include <buola/utility/cregion.h>

#include <buola/widgets/clistbox.h>
#include <buola/widgets/cscrollbar.h>
#include <buola/widgets/ceditbox.h>

#include "clbstriprenderer.h"

namespace buola { namespace gui {

CImageMatrix *CLBStripRenderer::mSelectFrame=nullptr;

class CLBStripRendererData
{
public:
    //preshape
    double mMinWidth;               //minimum width for the item
    CSize_d mIconSize;            

    //shape
    CSize_d mSize;                //actual size of the item

    //pos
    CPoint_d mPos;                //absolute position of the item
};

CLBStripRenderer::CLBStripRenderer(CListBox *pListBox)
            :CLBRenderer(pListBox)
{
    mFont=img::CFont::GetStock(L"control");

    for(int i=0;i<mListBox->GetItems().size();i++)
    {
        InitItem(mListBox->GetItems()[i]);
    }

    CalcAll();
}

CLBStripRenderer::~CLBStripRenderer()
{
    for(int i=0;i<mListBox->GetItems().size();i++)
    {
        DeinitItem(mListBox->GetItems()[i]);
    }
}

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//////////////////////// Event handling ///////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
///////////////////// Internal functions //////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////

void CLBStripRenderer::CalcAll()
{
    if(!mSelectFrame)
    {
        ///\todo
//        mSelectFrame=new CImageMatrix;
//        mSelectFrame->CreateRoundRect(gConfig.mColors.Get(L"selection_back"),7);
    }

    mIconSize=mListBox->GetImageSize();

    PreshapeAll();
    ShapeAll();
    PosAll();
}

void CLBStripRenderer::InitItem(CListBox::SItem *pI)
{
    CLBStripRendererData *lD=new CLBStripRendererData;
    
    pI->mRendererData=lD;
}

void CLBStripRenderer::DeinitItem(CListBox::SItem *pI)
{
    delete (CLBStripRendererData*)pI->mRendererData;
    pI->mRendererData=nullptr;
}        

void CLBStripRenderer::PreshapeItem(CListBox::SItem *pI)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;
    
    if(pI->mImage)
    {
        lD->mIconSize=pI->mImage.Size();
    }
    else
    {
        lD->mIconSize=mIconSize;
    }

    lD->mMinWidth=lD->mIconSize.x;

    mMaxMinWidth=max(mMaxMinWidth,lD->mMinWidth);
}

void CLBStripRenderer::ShapeItem(CListBox::SItem *pI)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;
    
    CPoint_d lPos(0,0);

//Shape icon
    lD->mSize.x=lD->mIconSize.x+8;
    lD->mSize.y=lD->mIconSize.y+8;
}

void CLBStripRenderer::DrawItem(img::CGC *pGC,CListBox::SItem *pI)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;
    
    CPoint_d lPos=lD->mPos-mListBox->GetScrollPos();

    if(pI->mSelected&&(mListBox->GetStyle()&CListBox::STYLE_SINGLECOLUMN))
    {
        ///\todo
//        mSelectFrame->Draw(pGC,CRect_d(lPos.x,lPos.y,
//                            lPos.x+lD->mSize.x,lPos.y+lD->mSize.y));
    }

    if(pI->mImage)
    {
        pGC->DrawImage(lPos+CPoint_d(4,4),pI->mImage);
    }
    else if(pI->mIcon)
    {
        pI->mIcon->Draw(pGC,lPos+CPoint_d(4,4),mListBox->GetIconSize(),
                        pI->mSelected?CIcon::SELECTED:CIcon::NORMAL);
    }
}

void CLBStripRenderer::RedrawItem(CListBox::SItem *pI,bool pRedraw)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;
    
    CRect_d lRect(lD->mPos-mListBox->GetScrollPos(),lD->mSize);

    img::CGC *lGC=mListBox->StartPainting(CRegion(lRect),pRedraw);
    lGC->SetFont(mFont);
    DrawItem(lGC,pI);

    if(mListBox->mSelecting&&mListBox->mLastDragPoint!=mListBox->mDragPoint)
    {
        CRegion lInt(lRect);
        lInt&=CRect_d(mListBox->mLastDragPoint,mListBox->mDragPoint);

        lGC->SetSource(img::CColor(0x7f7f6028));
        ///\todo lGC->FillRegion(lInt);
    }

    mListBox->EndPainting(lGC,CRegion(lRect));
}

int CLBStripRenderer::HitTestItem(CListBox::SItem *pI,const CPoint_d &pP)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;
    
    CPoint_d lP=pP-lD->mPos+mListBox->GetScrollPos();
    
    if(lP.x<0||lP.y<0||lP.x>=lD->mSize.x||lP.y>=lD->mSize.y)
        return LBHT_NONE;
    
    if(CRect_d(CPoint_d(0,0),lD->mIconSize).Contains(lP))
        return LBHT_REGION|LBHT_ICON|LBHT_SELECT;
    
    return LBHT_REGION; 
}

int CLBStripRenderer::HitTestItem(CListBox::SItem *pI,const CRect_d &pR)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;
    
    CRect_d lR=pR-lD->mPos+mListBox->GetScrollPos();
    
    if(lR.r<0||lR.b<0||lR.l>=lD->mSize.x||lR.t>=lD->mSize.y)
        return LBHT_NONE;
    
    if(lR.l<0&&lR.t<0&&lR.r>=lD->mSize.x&&lR.b>=lD->mSize.y)
        return LBHT_TOTAL;
    
    return LBHT_NONE;    
}

void CLBStripRenderer::PreshapeAll()
{
    mMaxMinWidth=0;
    for(int i=0;i<mListBox->GetItems().size();i++)
    {
        PreshapeItem(mListBox->GetItems()[i]);
    }
}

void CLBStripRenderer::ShapeAll()
{
    for(int i=0;i<mListBox->GetItems().size();i++)
    {
        ShapeItem(mListBox->GetItems()[i]);
    }
}

void CLBStripRenderer::PosAll(int /*pFrom*/)
{
    CSize_d lSize=mListBox->Size();

    double lX=0;

    for(int i=0;i<mListBox->GetItemCount();i++)
    {
        CListBox::SItem *lI=mListBox->GetItems()[i];
        CLBStripRendererData *lD=(CLBStripRendererData*)lI->mRendererData;

        lD->mPos.x=lX+4;
        lD->mPos.y=4;

        lX+=lD->mIconSize.x+8;                      
    }

    int lScrollRange=(int)(lX-lSize.x);

    if(lScrollRange<=0)
    {
        mListBox->GetHScroll()->SetRange(0,0);
        mListBox->GetHScroll()->Show(false);
    }
    else
    {
        mListBox->GetHScroll()->SetRange(0,lScrollRange);
        mListBox->GetHScroll()->SetStep(
                                (int)mIconSize.x,(int)lSize.x);
        mListBox->GetHScroll()->Show(true);
    }

    mListBox->GetVScroll()->SetRange(0,0);
    mListBox->GetVScroll()->Show(false);
}

void CLBStripRenderer::DrawAll(img::CGC *pGC)
{
    for(int i=0;i<mListBox->GetItemCount();i++)
    {
        CListBox::SItem *lI=mListBox->GetItems()[i];
        CLBStripRendererData *lD=(CLBStripRendererData*)lI->mRendererData;

        if(lD->mPos.x-mListBox->GetScrollPos().x>mListBox->Size().x+1||
           lD->mPos.x-mListBox->GetScrollPos().x+lD->mSize.x<0||
           lD->mPos.y-mListBox->GetScrollPos().y>mListBox->Size().y+1||
           lD->mPos.y-mListBox->GetScrollPos().y+lD->mSize.y<0) continue;
        
        DrawItem(pGC,lI);
    }
}

void CLBStripRenderer::DrawAll(img::CGC *pGC,const CRect_d &pRect)
{
    for(int i=0;i<mListBox->GetItemCount();i++)
    {
        CListBox::SItem *lI=mListBox->GetItems()[i];
        CLBStripRendererData *lD=(CLBStripRendererData*)lI->mRendererData;

        if(lD->mPos.x-mListBox->GetScrollPos().x>pRect.r+1||
           lD->mPos.x-mListBox->GetScrollPos().x+lD->mSize.x<pRect.l||
           lD->mPos.y-mListBox->GetScrollPos().y>pRect.b+1||
           lD->mPos.y-mListBox->GetScrollPos().y+lD->mSize.y<pRect.t) continue;
        
        DrawItem(pGC,lI);
    }
}

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
/////////////////////// Event handlers ////////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////

void CLBStripRenderer::OnSize(const CGeometryEvent &pE)
{
    CalcAll();
}

void CLBStripRenderer::OnExpose(const CExposeEvent &pE)
{
    img::CGC *lGC=pE.mGC;
    
    lGC->SetSource(gConfig.mColors.Get(L"edit_text"));
    lGC->SetFont(mFont);
    DrawAll(lGC,pE.mRect);
}    

void CLBStripRenderer::OnBigChange()
{
    CalcAll();
    mListBox->Refresh();
}

void CLBStripRenderer::OnItemSelected(CListBox::SItem *pItem)
{
    RedrawItem(pItem,true);
}

void CLBStripRenderer::OnItemAdded(CListBox::SItem *pItem)
{
    InitItem(pItem);
    double lOldMaxMinWidth=mMaxMinWidth;
    PreshapeItem(pItem);
    
    if(lOldMaxMinWidth!=mMaxMinWidth) //we must redo everything
    {
        ShapeAll();

        if(mListBox->GetAutoUpdate())
        {
            PosAll();
            mListBox->Refresh();
        }
        return;
    }

    ShapeItem(pItem);

    if(mListBox->GetAutoUpdate())
    {
        PosAll();
        RedrawItem(pItem,false);
    }
}

void CLBStripRenderer::OnItemChanged(CListBox::SItem *pItem)
{
    ShapeItem(pItem);

    if(mListBox->GetAutoUpdate())
    {
        PosAll();
        RedrawItem(pItem,true);
    }
}

void CLBStripRenderer::OnItemRemoved(CListBox::SItem *pI)
{
    DeinitItem(pI);
}

PEditBox CLBStripRenderer::Edit(CListBox::SItem*)
{
    return nullptr;
}

CPoint_d CLBStripRenderer::GetIconPos(CListBox::SItem *pI)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;

    return lD->mPos-mListBox->GetScrollPos();
}

CRect_d CLBStripRenderer::GetItemRect(CListBox::SItem *pI)
{
    CLBStripRendererData *lD=(CLBStripRendererData*)pI->mRendererData;

    return CRect_d(lD->mPos-mListBox->GetScrollPos(),lD->mSize);
}

/*namespace gui*/ } /*namespace buola*/ }
